<?php
class SevicesAlipay 
{
	/** 提示：如何获取安全校验码和合作身份者ID
	1.访问支付宝商户服务中心(b.alipay.com)，然后用您的签约支付宝账号登陆.
	2.访问“技术服务”→“下载技术集成文档”（https://b.alipay.com/support/helperApply.htm?action=selfIntegration）
	3.在“自助集成帮助”中，点击“合作者身份(Partner ID)查询”、“安全校验码(Key)查询”
	
	安全校验码查看时，输入支付密码后，页面呈灰色的现象，怎么办？
	解决方法：
	1、检查浏览器配置，不让浏览器做弹框屏蔽设置
	2、更换浏览器或电脑，重新登录查询。
	*/
	
	//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
	//合作身份者ID，以2088开头的16位纯数字
	private $partner		= "";
	
	//安全检验码，以数字和字母组成的32位字符
	private $key   			= "";
	
	//签约支付宝账号或卖家支付宝帐户
	private $seller_email	= "w";
	
	//交易过程中服务器通知的页面 要用 http://格式的完整路径，不允许加?id=123这类自定义参数
	private $notify_url		= "http://www.ailezi.com/pay/notify_url.do";
	
	//付完款后跳转的页面 要用 http://格式的完整路径，不允许加?id=123这类自定义参数
	private $return_url		= "http://www.ailezi.com/pay/return_url.do";
	
	//网站商品的展示地址，不允许加?id=123这类自定义参数
	public $show_url		= "";
	
	//收款方名称，如：公司名称、网站名称、收款人姓名等
	private $mainname		= "爱乐滋";
	
	//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
	
	
	
	//签名方式 不需修改
	private $sign_type		= "MD5";
	
	//字符编码格式 目前支持 GBK 或 utf-8
	private $_input_charset	= "utf-8";
	
	//访问模式,根据自己的服务器是否支持ssl访问，若支持请选择https；若不支持请选择http
	private $transport		= "http";
	
	
	//自定义
	private $seller_id = "";
	private $seller_account_name = "";
	
	
	
	
	public $out_trade_no = "";	//请与贵网站订单系统中的唯一订单号匹配
	public $subject      = "";	//订单名称，显示在支付宝收银台里的“商品名称”里，显示在支付宝的交易管理的“商品名称”的列表里。
	public $body         = "";	//订单描述、订单详细、订单备注，显示在支付宝收银台里的“商品描述”里
	public $total_fee    = "";	//订单总金额，显示在支付宝收银台里的“应付总额”里
	public $paymethod 	 = "";  //默认支付方式，四个值可选：bankPay(网银); cartoon(卡通); directPay(余额); CASH(网点支付)
	public $defaultbank  = "";  //默认网银代号，代号列表见http://club.alipay.com/read.php?tid=8681379
	
	//扩展功能参数——防钓鱼
	//请慎重选择是否开启防钓鱼功能
	//exter_invoke_ip、anti_phishing_key一旦被使用过，那么它们就会成为必填参数
	//开启防钓鱼功能后，服务器、本机电脑必须支持远程XML解析，请配置好该环境。
	//若要使用防钓鱼功能，请打开class文件夹中alipay_function.php文件，找到该文件最下方的query_timestamp函数，根据注释对该函数进行修改
	//建议使用POST方式请求数据
	public $anti_phishing_key  = '';			//防钓鱼时间戳
	public $exter_invoke_ip = '';				//获取客户端的IP地址，建议：编写获取客户端IP地址的程序
	//如：
	//$exter_invoke_ip = '202.1.1.1';
	//$anti_phishing_key = query_timestamp($partner);		//获取防钓鱼时间戳函数
	
	//扩展功能参数——其他
	public $extra_common_param = '';			//自定义参数，可存放任何内容（除=、&等特殊字符外），不会显示在页面上
	public $buyer_email		= '';			//默认买家支付宝账号
	
	//扩展功能参数——分润(若要使用，请按照注释要求的格式赋值)
	public $royalty_type		= "";			//提成类型，该值为固定值：10，不需要修改
	public $royalty_parameters	= "";
	//提成信息集，与需要结合商户网站自身情况动态获取每笔交易的各分润收款账号、各分润金额、各分润说明。最多只能设置10条
	//各分润金额的总和须小于等于total_fee
	//提成信息集格式为：收款方Email_1^金额1^备注1|收款方Email_2^金额2^备注2
	//如：
	//royalty_type = "10"
	//royalty_parameters	= "111@126.com^0.01^分润备注一|22
	
	private $gateway	      = "https://www.alipay.com/cooperate/gateway.do?"; //网关地址
	private $mysign;			//签名结果
	
	private $parameter;
	
	
	
	public function alipay_service() 
    {
    	$parameter = $this->create_parameter();
        $this->parameter      = $this->para_filter($parameter);

        $sort_array   = $this->arg_sort($this->parameter);    //得到从字母a到z排序后的签名参数数组
        $this->mysign = $this->build_mysign($sort_array,$this->key,$this->sign_type);

    }
	
 	public function build_form($auto = 0) 
 	{
 		$this->alipay_service();
		//GET方式传递
        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."_input_charset=".$this->parameter['_input_charset']."' method='get'>";
		//POST方式传递（GET与POST二必选一）
		//$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."_input_charset=".$this->parameter['_input_charset']."' method='post'>";

        while (list ($key, $val) = each ($this->parameter)) {
            $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
        }

        $sHtml = $sHtml."<input type='hidden' name='sign' value='".$this->mysign."'/>";
        $sHtml = $sHtml."<input type='hidden' name='sign_type' value='".$this->sign_type."'/>";

		//submit按钮控件请不要含有name属性
        $sHtml = $sHtml."<input type='submit' value='我现在就支付'></form>";
        //自动提交
		if($auto == 1)
		{
			$sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
		}
        return $sHtml;
    }
	
	
	
   public function create_parameter()
   {
   		$parameter = array(
	        "service"			=> "create_direct_pay_by_user",	//接口名称，不需要修改
	        "payment_type"		=> "1",               			//交易类型，不需要修改
	
	        //获取配置文件(alipay_config.php)中的值
	        "partner"			=> $this->partner,
	        "seller_email"		=> $this->seller_email,
	        "return_url"		=> $this->return_url,
	        "notify_url"		=> $this->notify_url,
	        "_input_charset"	=> $this->_input_charset,
	        "show_url"			=> $this->show_url,
	
	        //从订单数据中动态获取到的必填参数
	        "out_trade_no"		=> $this->out_trade_no,
	        "subject"			=> $this->subject,
	        "body"				=> $this->body,
	        "total_fee"			=> $this->total_fee,
	
	        //扩展功能参数——网银提前
	        "paymethod"			=> $this->paymethod,
	        "defaultbank"		=> $this->defaultbank,
	
	        //扩展功能参数——防钓鱼
	        "anti_phishing_key"	=> $this->anti_phishing_key,
			"exter_invoke_ip"	=> $this->exter_invoke_ip,
	
			//扩展功能参数——自定义参数
			"buyer_email"		=> $this->buyer_email,
	        "extra_common_param"=> $this->extra_common_param,
			
			//扩展功能参数——分润
	        "royalty_type"		=> $this->royalty_type,
	        "royalty_parameters"=> $this->royalty_parameters,
		);	
		return $parameter;
   }
	
	
	
	
	
	
	
	
	
	
	
	

	/**
	 *功能：支付宝接口公用函数
	 *详细：该页面是请求、通知返回两个文件所调用的公用函数核心处理文件，不需要修改
	 *版本：3.1
	 *修改日期：2010-10-29
	 '说明：
	 '以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
	 '该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
	
	*/
	 
	/**生成签名结果
	 *$array要签名的数组
	 *return 签名结果字符串
	*/
	function build_mysign($sort_array,$key,$sign_type = "MD5") 
	{
	    $prestr = $this->create_linkstring($sort_array);     	//把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
	    $prestr = $prestr.$key;							//把拼接后的字符串再与安全校验码直接连接起来
	    $mysgin = $this->sign($prestr,$sign_type);			    //把最终的字符串签名，获得签名结果
	    return $mysgin;
	}	
	
	/********************************************************************************/
	
	/**把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		*$array 需要拼接的数组
		*return 拼接完成以后的字符串
	*/
	function create_linkstring($array)
	{
	    $arg  = "";
	    while (list ($key, $val) = each ($array)) {
	        $arg.=$key."=".$val."&";
	    }
	    $arg = substr($arg,0,count($arg)-2);		     //去掉最后一个&字符
	    return $arg;
	}
	
	/********************************************************************************/
	
	/**除去数组中的空值和签名参数
		*$parameter 签名参数组
		*return 去掉空值与签名参数后的新签名参数组
	 */
	function para_filter($parameter) 
	{
	    $para = array();
	    while (list ($key, $val) = each ($parameter)) {
	        if($key == "sign" || $key == "sign_type" || $val == "")continue;
	        else	$para[$key] = $parameter[$key];
	    }
	    return $para;
	}
	
	/********************************************************************************/
	
	/**对数组排序
		*$array 排序前的数组
		*return 排序后的数组
	 */
	function arg_sort($array) 
	{
	    ksort($array);
	    reset($array);
	    return $array;
	}
	
	/********************************************************************************/
	
	/**签名字符串
		*$prestr 需要签名的字符串
		*return 签名结果
	 */
	function sign($prestr,$sign_type) 
	{
	    $sign='';
	    if($sign_type == 'MD5') {
	        $sign = md5($prestr);
	    }elseif($sign_type =='DSA') {
	        //DSA 签名方法待后续开发
	        die("DSA 签名方法待后续开发，请先使用MD5签名方式");
	    }else {
	        die("支付宝暂不支持".$sign_type."类型的签名方式");
	    }
	    return $sign;
	}
	
	/********************************************************************************/
	
	// 日志消息,把支付宝返回的参数记录下来
	// 请注意服务器是否开通fopen配置
	function  log_result($word) 
	{
	    $fp = fopen("../myfolder/log/alipaypay.log","a");
	    flock($fp, LOCK_EX) ;
	    fwrite($fp,"执行日期：".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n");
	    flock($fp, LOCK_UN);
	    fclose($fp);
	}	
	
	/********************************************************************************/
	
	/**实现多种字符编码方式
		*$input 需要编码的字符串
		*$_output_charset 输出的编码格式
		*$_input_charset 输入的编码格式
		*return 编码后的字符串
	 */
	function charset_encode($input,$_output_charset ,$_input_charset) 
	{
	    $output = "";
	    if(!isset($_output_charset) )$_output_charset  = $_input_charset;
	    if($_input_charset == $_output_charset || $input ==null ) {
	        $output = $input;
	    } elseif (function_exists("mb_convert_encoding")) {
	        $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
	    } elseif(function_exists("iconv")) {
	        $output = iconv($_input_charset,$_output_charset,$input);
	    } else die("sorry, you have no libs support for charset change.");
	    return $output;
	}
	
	/********************************************************************************/
	
	/**实现多种字符解码方式
		*$input 需要解码的字符串
		*$_output_charset 输出的解码格式
		*$_input_charset 输入的解码格式
		*return 解码后的字符串
	 */
	function charset_decode($input,$_input_charset ,$_output_charset) 
	{
	    $output = "";
	    if(!isset($_input_charset) )$_input_charset  = $_input_charset ;
	    if($_input_charset == $_output_charset || $input ==null ) {
	        $output = $input;
	    } elseif (function_exists("mb_convert_encoding")) {
	        $output = mb_convert_encoding($input,$_output_charset,$_input_charset);
	    } elseif(function_exists("iconv")) {
	        $output = iconv($_input_charset,$_output_charset,$input);
	    } else die("sorry, you have no libs support for charset changes.");
	    return $output;
	}
	
	/*********************************************************************************/
	
	/**用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数
	注意：由于低版本的PHP配置环境不支持远程XML解析，因此必须服务器、本地电脑中装有支持DOMDocument、SSL的PHP配置环境。建议本地调试时使用PHP开发软件
	*$partner 合作身份者ID
	*return 时间戳字符串
	*/
	function query_timestamp($partner) 
	{
	    $URL = "https://mapi.alipay.com/gateway.do?service=query_timestamp&partner=".$partner;
		$encrypt_key = "";
	//若要使用防钓鱼，请取消下面的4行注释
	//    $doc = new DOMDocument();
	//    $doc->load($URL);
	//    $itemEncrypt_key = $doc->getElementsByTagName( "encrypt_key" );
	//    $encrypt_key = $itemEncrypt_key->item(0)->nodeValue;
	//    return $encrypt_key;
	}
	
	/**对notify_url的认证
	*返回的验证结果：true/false
     */
    function notify_verify() {
    	
    	 if($this->transport == "https") {
            $gateway = "https://www.alipay.com/cooperate/gateway.do?";
        }else {
            $gateway = "http://notify.alipay.com/trade/notify_query.do?";
        }
        //获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
        if($this->transport == "https") {
            $veryfy_url = $gateway. "service=notify_verify" ."&partner=" .$this->partner. "&notify_id=".$_POST["notify_id"];
        } else {
            $veryfy_url = $gateway. "partner=".$this->partner."&notify_id=".$_POST["notify_id"];
        }
        $veryfy_result = $this->get_verify($veryfy_url);
        //生成签名结果
		if(empty($_POST)) {							//判断POST来的数组是否为空
			return false;
		}
		else {		
			$post          = $this->para_filter($_POST);	    //对所有POST返回的参数去空
			$sort_post     = $this->arg_sort($post);	    //对所有POST反馈回来的数据排序
			$this->mysign  = $this->build_mysign($sort_post,$this->key,$this->sign_type);   //生成签名结果
	
			//写日志记录
			$this->log_result("veryfy_result=".$veryfy_result."\n notify_url_log:sign=".$_POST["sign"]."&mysign=".$this->mysign.",".$this->create_linkstring($sort_post));
	
			//判断veryfy_result是否为ture，生成的签名结果mysign与获得的签名结果sign是否一致
			//$veryfy_result的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
			//mysign与sign不等，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
			if (preg_match("/true$/i",$veryfy_result) && $this->mysign == $_POST["sign"]) {
				return true;
			} else {
				return false;
			}
		}
    }
 	function return_verify() {
 	 if($this->transport == "https") {
            $gateway = "https://www.alipay.com/cooperate/gateway.do?";
        }else {
            $gateway = "http://notify.alipay.com/trade/notify_query.do?";
        }
        //获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
        if($this->transport == "https") {
            $veryfy_url = $gateway. "service=notify_verify" ."&partner=" .$this->partner. "&notify_id=".$_GET["notify_id"];
        } else {
            $veryfy_url = $gateway. "partner=".$this->partner."&notify_id=".$_GET["notify_id"];
        }
        $veryfy_result = $this->get_verify($veryfy_url);
        //生成签名结果
		if(empty($_GET)) {							//判断GET来的数组是否为空
			return false;
		}
		else {
			$get          = $this->para_filter($_GET);	    //对所有GET反馈回来的数据去空
			$sort_get     = $this->arg_sort($get);		    //对所有GET反馈回来的数据排序
			
			$this->mysign  = $this->build_mysign($sort_get,$this->key,$this->sign_type);    //生成签名结果
			//写日志记录
			//log_result("veryfy_result=".$veryfy_result."\n return_url_log:sign=".$_GET["sign"]."&mysign=".$this->mysign."&".create_linkstring($sort_get));
	
			//判断veryfy_result是否为ture，生成的签名结果mysign与获得的签名结果sign是否一致
			//$veryfy_result的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
			//mysign与sign不等，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
			if (preg_match("/true$/i",$veryfy_result) && $this->mysign == $_GET["sign"]) {            
				return true;
			}else {
				return false;
			}
		}
    }
 	function get_verify($url,$time_out = "60") {
        $urlarr     = parse_url($url);
        $errno      = "";
        $errstr     = "";
        $transports = "";
        if($urlarr["scheme"] == "https") {
            $transports = "ssl://";
            $urlarr["port"] = "443";
        } else {
            $transports = "tcp://";
            $urlarr["port"] = "80";
        }
        $fp=@fsockopen($transports . $urlarr['host'],$urlarr['port'],$errno,$errstr,$time_out);
        if(!$fp) {
            die("ERROR: $errno - $errstr<br />\n");
        } else {
            fputs($fp, "POST ".$urlarr["path"]." HTTP/1.1\r\n");
            fputs($fp, "Host: ".$urlarr["host"]."\r\n");
            fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
            fputs($fp, "Content-length: ".strlen($urlarr["query"])."\r\n");
            fputs($fp, "Connection: close\r\n\r\n");
            fputs($fp, $urlarr["query"] . "\r\n\r\n");
            while(!feof($fp)) {
                $info[]=@fgets($fp, 1024);
            }
            fclose($fp);
            $info = implode(",",$info);
            return $info;
        }
    }
}
?>